package com.poizon.infrastructure.idem.core.annoations.aop;

import cn.hutool.core.lang.Assert;
import com.alibaba.fastjson.JSON;
import com.poizon.infrastructure.common.util.CustomSpelParseUtil;
import com.poizon.infrastructure.idem.core.annoations.Idempotent;
import com.poizon.infrastructure.idem.core.util.IIdemUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * AOP/幂等注解增强
 */
@Slf4j
@Aspect
@AllArgsConstructor
public class IdempotentAspect {
    private IIdemUtil idemUtil;

    /**
     * 注解拦截/幂等
     */
    @Around("@annotation(idempotent)")
    public Object handleIdempotent(ProceedingJoinPoint joinPoint, Idempotent idempotent) {
        Assert.notNull(joinPoint);
        Assert.notNull(idempotent);
        Assert.notBlank(idempotent.value(), "幂等key不能为空字符串");

        String spelExpression = idempotent.value();
        long expireTime = idempotent.expireTime();
        int retryTimes = idempotent.retryTimes();
        TimeUnit timeUnit = idempotent.timeUnit();
        String reqType = idempotent.reqType();

        String idemKey = CustomSpelParseUtil.parseSpelValue(joinPoint, spelExpression);

        if (log.isDebugEnabled()) {
            log.debug(">spel表达式幂等键解析 returned: " + idemKey);
        }

        Object obj = idemUtil.executeIdem(
                idemKey,
                () -> {
                    try {
                        return joinPoint.proceed();
                    } catch (Throwable e) {
                        throw new RuntimeException(e.getMessage());
                    }
                },
                expireTime,
                timeUnit,
                reqType,
                new AtomicInteger(retryTimes)
        );

        if (log.isDebugEnabled()) {
            log.debug(">幂等执行完毕，返回结果:{}", JSON.toJSONString(obj, true));
        }

        return obj;
    }


}
